package com.lagou.edu.service.impl;

import com.lagou.edu.annotation.AutoWired;
import com.lagou.edu.annotation.MyTransational;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.config.BeanDefinition;
import com.lagou.edu.constant.Constant;
import com.lagou.edu.factory.CglibProxy;
import com.lagou.edu.factory.ProxyFactory;
import com.lagou.edu.service.ApplicationContext;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public  class AnnotationConfigApplicationContext  implements ApplicationContext {

    /**
     * 存储bean单例
     */
    public final static Map<String, BeanDefinition> beanMap = new HashMap<>();

    /**
     * 先把包名转换为路径,首先得到项目的classpath
     */
    String path=this.getClass().getResource("/").getPath().replaceAll("/","\\\\");
    private final String CLASSPATH = path.substring(1);

    public AnnotationConfigApplicationContext(String... basePackages) {
        try {
            scanPackages(basePackages);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void scanPackages(String[] basePackages) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        // 获取所有配置Service注解的对象
        setBeanDefinition(basePackages);
        // 设置依赖关系
        setBeanDepend();
        // 设置事务增强
        setTransaction();
    }



    private void setBeanDefinition(String[] basePackages) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 获取所有类路径下的class文件
        List<String> classPaths = new ArrayList<>();
        for (String basePackage : basePackages) {
            //然后把我们的包名basPath转换为路径名
            basePackage = basePackage.replace(".", File.separator);
            //然后把classpath和basePack合并
            String searchPath = CLASSPATH + basePackage;
            getFile(classPaths, new File(searchPath));
        }

        // 找出所有有@Service注解的类，加入到beanMap中
        for (String s : classPaths) {
            s = s.replace(CLASSPATH, "").replaceAll("\\\\", ".").replace(".class", "");
            Class clazz = Class.forName(s);
            Service service = (Service) clazz.getAnnotation(Service.class);
            if (service != null) {
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setBeanObject(clazz.newInstance());

                // 设置bean name
                String value = service.value();
                // 如果没有自定义bean name，设置bean name为class name
                if ("".equals(value)) {
                    value = clazz.getName();
                    value = value.substring(value.lastIndexOf(".") + 1);
                    value = value.substring(0, 1).toLowerCase() + value.substring(1);
                }
                beanDefinition.setBeanName(value);

                // 是否有依赖关系
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    declaredField.setAccessible(true);
                    //解析AutoWired注解
                    AutoWired autowired = declaredField.getAnnotation(AutoWired.class);
                    // 如果类属性有@Autowired注解，把依赖对象放入BeanDefinition的dependBeanName集合中
                    if (autowired != null) {
                        beanDefinition.setAutoWired(true);
                        beanDefinition.getDependBeanName().add(declaredField.getName());
                    }
                }

                // 是否有父接口，为后面增强使用动态代理还是cglib代理做准备
                Class[] interfaces = clazz.getInterfaces();
                if (interfaces.length > 0) {
                    beanDefinition.setHaveParent(true);
                }

                // 是否有事务注解，如果有加入到BeanDefinition对象的transactionMethods属性中
                Annotation annotation = clazz.getAnnotation(MyTransational.class);
                Method[] methods = clazz.getDeclaredMethods();
                //如果@Transactional注解出现在类上面，把类下所有的方法都加入到待添加事务的列表中
                if (annotation != null) {
                    for (Method method : methods) {
                        beanDefinition.getTransactionMethods().add(method);
                    }
                } else {
                    // 如果@Transactional在某个方法上面，把该方法加入到BeanDefinition对象的transactionMethods属性中
                    for (Method method : methods) {
                        MyTransational methodAnnotation = method.getAnnotation(MyTransational.class);
                        if (methodAnnotation != null) {
                            beanDefinition.getTransactionMethods().add(method);
                        }
                    }
                }
                // 将添加事务之后的对象重新加入到beanMap中
                beanMap.put(beanDefinition.getBeanName(), beanDefinition);
            }
        }

    }

    /**
     * 设置bean的依赖关系
     */
    private void setBeanDepend() {
        for (Map.Entry<String, BeanDefinition> next : beanMap.entrySet()) {
            BeanDefinition beanDefinition = next.getValue();
            //如果有AutoWired注解，设置值
            if (beanDefinition.isAutoWired()) {
                Object object = beanDefinition.getBeanObject();
                Class<?> definitionClass = object.getClass();
                List<String> beanNames = beanDefinition.getDependBeanName();
                beanNames.forEach(w -> {
                    try {
                        // 获取需要进行注入的对象
                        Field declaredField = definitionClass.getDeclaredField(w);
                        // 暴力访问
                        declaredField.setAccessible(true);
                        // 给依赖对象赋值
                        declaredField.set(object, getBean(w));
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                });
                // 组装好的对象重新放入map中
                beanMap.put(beanDefinition.getBeanName(), beanDefinition);
            }
        }
    }

    private void setTransaction() {
        for (Map.Entry<String, BeanDefinition> next : beanMap.entrySet()) {
            BeanDefinition definition = next.getValue();
//            if(definition.getBeanName().contains("Dao")){
//                return;
//            }
            //获取需要开启事务管理的类下的所有方法
            List<Method> transactionMethods = definition.getTransactionMethods();
            ProxyFactory proxyFactory = new ProxyFactory();
            // 根据代理对象是实现接口，来决定采用动态代理还是cglib代理
            if (!definition.isHaveParent()) {
                proxyFactory = new ProxyFactory(new CglibProxy());
            }
            //给方法配置事务增强
            Object withTransaction = proxyFactory.getProxyWithTransaction(definition.getBeanObject(), transactionMethods);
            definition.setBeanObject(withTransaction);
            //把增强之后的对象重新放入到beanMap中
            beanMap.put(next.getKey(), definition);
        }

    }

    private void getFile(List<String> classPaths, File file) {
        //文件夹递归
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f1 : files) {
                    getFile(classPaths, f1);
                }
            }
        } else {
            //标准文件获取class文件
            if (file.getName().endsWith(Constant.FILE_TYPE_CLASS)) {
                //如果是class文件我们就放入我们的集合中。
                classPaths.add(file.getPath());
            }
        }
    }


    @Override
    public Object getBean(String id) {
        return beanMap.get(id).getBeanObject();
    }

    @Override
    public <T> T getBean(String name,Class<T> clazz) {
        Object o=beanMap.get(name).getBeanName();
    if(o.getClass() == clazz){
        return (T)o;
    }
        return null;
    }
}
